/*
 * IntList.c
 *
 *  Created on: 2011-9-17
 *      Author: Jesse Meng [pingf0@gmail.com]
 */
#include "IntList.h"


static int IntListNode_reload(IntListNode *self,unsigned int p){
	self->prev=self->next=NULL;
	self->value=p;
	return 0;
}

ASM_C0(IntListNode,IntListNode_reload,NULL)


static int IntList_isEmpty(IntList *self){
	return self->len==0?1:0;
}

static int IntList_isHead(IntList *self,IntListNode *node){
	if(self->head==NULL) return (-1);
	return self->head==node?1:0;
}

static int IntList_isTail(IntList *self,IntListNode *node){
	if(self->tail==NULL) return (-1);
	return self->tail==node?1:0;
}

static int IntList_len(IntList *self){
	return self->len;
}
static int IntList_head(IntList *self){
	return self->head->value;
}
static int IntList_tail(IntList *self){
	return self->tail->value;
}
static void IntList_zero(IntList *self){
	self->len=0;
	self->head=NULL;
	self->tail=NULL;
}
static void IntListNode_connect(IntListNode *l,IntListNode *r){
	l->next=r;
	r->prev=l;
}


static IntListNode* IntList_nodeAtRaw(IntList *self,unsigned int offset){
	IntListNode *node=NULL;
	unsigned int i=0;
	if(offset<self->len/2){
		node=self->head;
		while(i<offset){
			node=node->next;
			++i;
		}
	}else{
		node=self->tail;
		i=self->len-1;
		while(i>offset){
			node=node->prev;
			--i;
		}
	}
	return node;
}

static IntListNode* IntList_nodeAt(IntList *self,int *offset){
	//isOffsetValid 不同于下面的at方法中的检测
	if(*offset<0) {
		*offset+=(int)(self->len);
	}
	if(*offset>=self->len) {
		return  NULL;
	}
	//
	return IntList_nodeAtRaw(self,*offset);
}


//不应在空时调用
static int IntList_at(IntList *self,int offset){
	//
	IntListNode *node=IntList_nodeAt(self,&offset);
	if(NULL==node) return 0;
	return node->value;
}


static int IntList_atEx(IntList *self,int offset,int *ok){
	//
	IntListNode *node=IntList_nodeAt(self,&offset);
	*ok=1;
	if(NULL==node) {
		*ok=0;
		return 0;
	}
	return node->value;
}

//iFn insert,extend,remove,at,find,head,tail,view,isEmpty;
//iFn go2,seek,peek,isHead,isTail;

static int IntList_eachDoIndexForward(IntList *self,iFn each_do,void *param){
	unsigned int ret=0;
	unsigned int i=0;
	IntListNode *node=NULL;
	if(NULL==each_do){
		return (-1);
	}
	node=self->head;
	while(i<self->len){
		if((ret=each_do(node,param))<0) return (-1);
		else{
			if(ret>0) return i;
		}
		node=node->next;
		++i;
	}
	return ret;
}
static int IntList_eachDoIndexBackward(IntList *self,iFn each_do,void *param){
	unsigned int ret=0;
	unsigned int i=0;
	IntListNode *node=NULL;
	if(NULL==each_do){
		return (-1);
	}
	node=self->tail;
	while(i<self->len){
		if((ret=each_do(node,param))<0) return (-1);
		else{
			if(ret>0) return i;
		}
		node=node->prev;
		++i;
	}
	return ret;
}
static int IntList_eachDoReturnForward(IntList *self,iFn each_do,void *param){
	unsigned int ret=0;
	unsigned int i=0;
	IntListNode *node=NULL;
	if(NULL==each_do){
		return (-1);
	}
	node=self->head;
	while(i<self->len){
		if((ret=each_do(node,param))<0) return (-1);
		else{
			if(ret>0) return ret;
		}
		node=node->next;
		++i;
	}
	return ret;
}
static int IntList_eachDoReturnBackward(IntList *self,iFn each_do,void *param){
	unsigned int ret=0;
	unsigned int i=0;
	IntListNode *node=NULL;
	if(NULL==each_do){
		return (-1);
	}
	node=self->tail;
	while(i<self->len){
		if((ret=each_do(node,param))<0) return (-1);
		else{
			if(ret>0) return ret;
		}
		node=node->prev;
		++i;
	}
	return 0;
}

static int IntList_eachDoAccumulateForward(IntList *self,iFn each_do,void *param){
	unsigned int ret=0;
	unsigned int i=0;
	IntListNode *node=NULL;
	if(NULL==each_do){
		return (-1);
	}
	node=self->head;
	while(i<self->len){
		ret+=each_do(node,param);
		node=node->next;
		++i;
	}
	return 0;
}
static int IntList_eachDoAccumulateBackward(IntList *self,iFn each_do,void *param){
	unsigned int ret=0;
	unsigned int i=0;
	IntListNode *node=NULL;
	if(NULL==each_do){
		return (-1);
	}
	node=self->tail;
	while(i<self->len){
		ret+=each_do(node,param);
		node=node->prev;
		++i;
	}
	return ret;
}
static int IntList_remove1(IntList *self,int offset){
	IntListNode * node=NULL;
	if(self->len==0) return (-1);

	node=IntList_nodeAt(self,&offset); //EMPTY CHECKED
	if(NULL==node) return (-1);

	if(offset==0){
		self->head=node->next;
		node->next=NULL;
		--self->len;
		return DEL0(node,IntListNode);
	}

	if(offset==self->len-1){
		self->tail=node->prev;
		node->prev=NULL;
		--self->len;
		return DEL0(node,IntListNode);
	}

	IntListNode_connect(node->prev,node->next);
	--self->len;
	return DEL0(node,IntListNode);
}

static int IntList_insert1(IntList *self,int offset,unsigned int value){
	IntListNode * node=NULL,*node_new=NULL;
	node_new=NEW(IntListNode,(void *)value);

	if(self->len==0){
		self->head=node;
		self->tail=node;
		self->len=1;
		return 0;
	}

	if(offset==0){
		IntListNode_connect(node_new,self->head);
		self->head=node_new;
		++self->len;
		return 0;
	}
	if(offset==self->len){
		IntListNode_connect(self->tail,node_new);
		self->tail=node_new;
		++self->len;
		return 0;
	}

	node=IntList_nodeAt(self,&offset);
	if(NULL==node||NULL==node_new) return (-1);
	IntListNode_connect(node->prev,node_new);
	IntListNode_connect(node_new,node);
	++self->len;
	return 0;
}

static int IntList_view(IntList *self){
	unsigned int i=0;
	IntListNode *node=self->head;
	if(self->len==0) {
		printf("list is empty!\n");
		return 0;
	}
	while(i<self->len){
		printf("%d ",node->value);
		node=node->next;
		++i;
	}
	printf("\n");
	return 0;
}

static int IntList_reload(IntList *self,ArrayPack *p){
	unsigned int i=0;
	IntListNode *node=NULL,*hold=NULL;
	if(NULL==p||p->len==0) {
		IntList_zero(self);
		return 0;
	}
	self->len=p->len;
	while(i<self->len){
		node=NEW(IntListNode,(void *)((unsigned int *)p->data)[i]);
		if(NULL==node) return (-1);
		if(NULL!=hold){
			IntListNode_connect(hold,node);
		}else{
			self->head=node;
		}
		hold=node;
		++i;
	}
	self->tail=node;
	return 0;
}
static int IntList_extend(IntList *self,int offset,IntList *ex){
	IntListNode *node=NULL;
	//
	if(self->len==0){
		return (-1);
	}
	if(offset==0){
		IntListNode_connect(ex->tail,self->head);
		self->head=ex->head;
		self->len+=ex->len;
		return 0;
	}
	if(offset==self->len){
		IntListNode_connect(self->tail,ex->head);
		self->tail=ex->tail;
		self->len+=ex->len;
		return 0;
	}

	node=IntList_nodeAt(self,&offset);
	if(NULL==node) return (-1);
	IntListNode_connect(node->prev,ex->head);
	IntListNode_connect(ex->tail,node);
	self->len+=ex->len;
	return 0;
}

static int IntList_insert(IntList *self,int offset,unsigned int * data,unsigned int len){
	IntList l;
	if(0>INI(&l,IntList,PP(AP,.len=len,.data=data))) return (-1);
	//
	return IntList_extend(self,offset,&l);
}


static int IntList_unload(IntList *self,void *p){
	IntListNode *node1=NULL,*node2=NULL;
	unsigned int i=0,len=self->len;
	if(len<0) return (-1);
	if(0==len) return (0);
	node1=node2=self->head;
	while(i<len){
		node2=node1->next;
		if(0>DEL0(node1,IntListNode)) return (-1);
		node1=node2;
		++i;
	}
	self->head=self->tail=NULL;
	self->len=0;
	return 0;
}

static int IntList_remove(IntList *self,int offset,int range){
	IntList l;
	IntListNode *node=NULL;
	unsigned int i=0;
	if(self->len==0) return (-1);
	if(0>INI0(&l,IntList)) return (-1);
	if(0==range) return (-1);
	else if(1==range||-1==range) return IntList_remove1(self,offset);
	else if(range<0){
		l.tail=node=IntList_nodeAt(self,&offset);
		if(NULL==node) return (-1);
		if(offset==self->len-1&&range==0-self->len){
			return IntList_unload(self,NULL);
		}
		while(i-(range+1)>0){
			node=node->prev;
			if(NULL==node) return (-1);
			--i;
		}
		l.head=node;
		l.len=0-range;

		if(offset+range==0){
			self->head=l.tail->next;
			self->len-=l.len;
			return FIN0(&l,IntList);
		}
		if(offset==self->len-1){
			self->tail=l.head->prev;
			self->len-=l.len;
			return FIN0(&l,IntList);
		}
	}else{
		l.head=node=IntList_nodeAt(self,&offset);
		if(NULL==node) return (-1);
		if(offset==self->len-1&&range==self->len){
			return IntList_unload(self,NULL);
		}
		while(i<range-1){
			node=node->next;
			if(NULL==node) return (-1);
			++i;
		}
		l.tail=node;
		l.len=range;

		if(offset==0){
			self->head=l.tail->next;
			self->len-=l.len;
			return FIN0(&l,IntList);
		}
		if(offset+range==self->len){
			self->tail=l.head->prev;
			self->len-=l.len;
			return FIN0(&l,IntList);
		}
	}

	self->len-=l.len;
	IntListNode_connect(l.head->prev,l.tail->next);
	return FIN0(&l,IntList);
}
static int IntList_eachSelect(int type){
	IntList l;
	if(INI0(&l,IntList)<0) return (-1);

	if(type==-3){
		STT(&l,IntList)->eachDo=(void *)IntList_eachDoAccumulateBackward;
	}else if(type==-2){
		STT(&l,IntList)->eachDo=(void *)IntList_eachDoReturnBackward;
	}else if(type==-1){
		STT(&l,IntList)->eachDo=(void *)IntList_eachDoIndexBackward;
	}else if(type==1){
		STT(&l,IntList)->eachDo=(void *)IntList_eachDoIndexForward;
	}else if(type==2){
		STT(&l,IntList)->eachDo=(void *)IntList_eachDoReturnForward;
	}else if(type==3){
		STT(&l,IntList)->eachDo=(void *)IntList_eachDoAccumulateForward;
	}else{
		return (-1);
	}

	return FIN0(&l,IntList);
}


static int IntList_push(IntList *self,unsigned int v){
	return IntList_insert1(self,-1,v);
}
static int IntList_pop(IntList *self){
	return IntList_remove1(self,-1);
}
static int IntList_enQueue(IntList *self,unsigned int v){
	return IntList_insert1(self,-1,v);
}
static int IntList_deQueue(IntList *self){
	return IntList_remove1(self,0);
}
static int IntList_shiftLeft(IntList *self){
	IntListNode *head=NULL,*tail=NULL;
	if(self->len==0) return (-1);
	if(self->len==1) return (0);
	head=self->head;
	tail=self->tail;
	self->head=head->next;
	self->head->prev=NULL;
	IntListNode_connect(tail,head);
	self->tail=head;
	return 0;
}
static int IntList_shiftRight(IntList *self){
	IntListNode *head=NULL,*tail=NULL;
	if(self->len==0) return (-1);
	if(self->len==1) return (0);
	head=self->head;
	tail=self->tail;
	self->tail=tail->prev;
	self->tail->next=NULL;
	IntListNode_connect(tail,head);
	self->head=tail;
	return 0;
}



static int IntList_reloadSt(StIntList *self,void *p){
	self->view=(void *)IntList_view;
	self->at=(void *)IntList_at;
	self->insert1=(void *)IntList_insert1;
	self->insert=(void *)IntList_insert;
	self->extend=(void *)IntList_extend;
	self->remove1=(void *)IntList_remove1;
	self->remove=(void *)IntList_remove;
	self->eachSel=(void *)IntList_eachSelect;
	self->eachDo=(void *)IntList_eachDoIndexForward;
	//
	self->isEmpty=(void *)IntList_isEmpty;
	self->isHead=(void *)IntList_isHead;
	self->isTail=(void *)IntList_isTail;
	//
	self->len=(void *)IntList_len;
	self->head=(void *)IntList_head;
	self->tail=(void *)IntList_tail;
	//
	self->push=(void *)IntList_push;
	self->pop=(void *)IntList_pop;
	self->enQ=(void *)IntList_enQueue;
	self->deQ=(void *)IntList_deQueue;
	self->shiftL=(void *)IntList_shiftLeft;
	self->shiftR=(void *)IntList_shiftRight;
	//
	self->atEx=(void *)IntList_atEx;
	return 0;
}




ASM(IntList,IntList_reload,IntList_unload,IntList_reloadSt,NULL)
